#include "viml/builtin_file.h"
#include "viml/value.h"
#include <fstream>
#include <sstream>
#include <filesystem>
#include <system_error>
#include <stdexcept>
#include <ctime>

namespace fs = std::filesystem;

namespace viml {

// 辅助函数：验证路径参数
static std::string validate_path_arg(const Value& arg, const std::string& func_name) {
    if (!arg.is_string()) {
        throw std::runtime_error(func_name + "() path argument must be a string");
    }
    std::string path = arg.as_string();
    if (path.empty()) {
        throw std::runtime_error(func_name + "() path cannot be empty");
    }
    return path;
}

// ========================================
// 文件读写操作实现
// ========================================

Value file_read_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_read() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_read");
    
    try {
        std::ifstream file(path, std::ios::binary);
        if (!file.is_open()) {
            throw std::runtime_error("file_read() failed: cannot open file: " + path);
        }
        
        std::stringstream buffer;
        buffer << file.rdbuf();
        
        return Value::string(buffer.str());
    } catch (const std::exception& e) {
        throw std::runtime_error("file_read() failed: " + std::string(e.what()));
    }
}

Value file_write_builtin(const std::vector<Value>& args) {
    if (args.size() != 2) {
        throw std::runtime_error("file_write() requires exactly 2 arguments");
    }
    
    std::string path = validate_path_arg(args[0], "file_write");
    
    if (!args[1].is_string()) {
        throw std::runtime_error("file_write() content argument must be a string");
    }
    std::string content = args[1].as_string();
    
    try {
        std::ofstream file(path, std::ios::binary | std::ios::trunc);
        if (!file.is_open()) {
            throw std::runtime_error("file_write() failed: cannot open file: " + path);
        }
        
        file << content;
        file.close();
        
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("file_write() failed: " + std::string(e.what()));
    }
}

Value file_append_builtin(const std::vector<Value>& args) {
    if (args.size() != 2) {
        throw std::runtime_error("file_append() requires exactly 2 arguments");
    }
    
    std::string path = validate_path_arg(args[0], "file_append");
    
    if (!args[1].is_string()) {
        throw std::runtime_error("file_append() content argument must be a string");
    }
    std::string content = args[1].as_string();
    
    try {
        std::ofstream file(path, std::ios::binary | std::ios::app);
        if (!file.is_open()) {
            throw std::runtime_error("file_append() failed: cannot open file: " + path);
        }
        
        file << content;
        file.close();
        
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("file_append() failed: " + std::string(e.what()));
    }
}

Value file_readlines_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_readlines() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_readlines");
    
    try {
        std::ifstream file(path);
        if (!file.is_open()) {
            throw std::runtime_error("file_readlines() failed: cannot open file: " + path);
        }
        
        Value result = Value::list();
        auto& lines = result.as_list();
        
        std::string line;
        while (std::getline(file, line)) {
            lines.push_back(Value::string(line));
        }
        
        return result;
    } catch (const std::exception& e) {
        throw std::runtime_error("file_readlines() failed: " + std::string(e.what()));
    }
}

Value file_writelines_builtin(const std::vector<Value>& args) {
    if (args.size() != 2) {
        throw std::runtime_error("file_writelines() requires exactly 2 arguments");
    }
    
    std::string path = validate_path_arg(args[0], "file_writelines");
    
    if (!args[1].is_list()) {
        throw std::runtime_error("file_writelines() lines argument must be a list");
    }
    
    const auto& lines = args[1].as_list();
    
    try {
        std::ofstream file(path, std::ios::trunc);
        if (!file.is_open()) {
            throw std::runtime_error("file_writelines() failed: cannot open file: " + path);
        }
        
        for (const auto& line : lines) {
            if (!line.is_string()) {
                throw std::runtime_error("file_writelines() all lines must be strings");
            }
            file << line.as_string() << '\n';
        }
        
        file.close();
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("file_writelines() failed: " + std::string(e.what()));
    }
}

// ========================================
// 文件属性查询实现
// ========================================

Value file_exists_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_exists() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_exists");
    
    try {
        return Value::boolean(fs::exists(path));
    } catch (const std::exception&) {
        return Value::boolean(false);
    }
}

Value file_isfile_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_isfile() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_isfile");
    
    try {
        return Value::boolean(fs::is_regular_file(path));
    } catch (const std::exception&) {
        return Value::boolean(false);
    }
}

Value file_isdir_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_isdir() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_isdir");
    
    try {
        return Value::boolean(fs::is_directory(path));
    } catch (const std::exception&) {
        return Value::boolean(false);
    }
}

Value file_size_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_size() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_size");
    
    try {
        if (!fs::exists(path)) {
            throw std::runtime_error("file_size() failed: file not found: " + path);
        }
        
        auto size = fs::file_size(path);
        return Value::make_number(static_cast<int64_t>(size));
    } catch (const std::exception& e) {
        throw std::runtime_error("file_size() failed: " + std::string(e.what()));
    }
}

Value file_mtime_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_mtime() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_mtime");
    
    try {
        if (!fs::exists(path)) {
            throw std::runtime_error("file_mtime() failed: file not found: " + path);
        }
        
        auto ftime = fs::last_write_time(path);
        auto sctp = std::chrono::time_point_cast<std::chrono::system_clock::duration>(
            ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()
        );
        auto time = std::chrono::system_clock::to_time_t(sctp);
        
        return Value::make_number(static_cast<int64_t>(time));
    } catch (const std::exception& e) {
        throw std::runtime_error("file_mtime() failed: " + std::string(e.what()));
    }
}

// ========================================
// 目录操作实现
// ========================================

Value dir_create_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("dir_create() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "dir_create");
    
    try {
        fs::create_directories(path);
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("dir_create() failed: " + std::string(e.what()));
    }
}

Value dir_remove_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("dir_remove() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "dir_remove");
    
    try {
        if (!fs::exists(path)) {
            throw std::runtime_error("dir_remove() failed: directory not found: " + path);
        }
        
        if (!fs::is_directory(path)) {
            throw std::runtime_error("dir_remove() failed: not a directory: " + path);
        }
        
        fs::remove(path);
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("dir_remove() failed: " + std::string(e.what()));
    }
}

Value dir_list_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("dir_list() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "dir_list");
    
    try {
        if (!fs::exists(path)) {
            throw std::runtime_error("dir_list() failed: directory not found: " + path);
        }
        
        if (!fs::is_directory(path)) {
            throw std::runtime_error("dir_list() failed: not a directory: " + path);
        }
        
        Value result = Value::list();
        auto& files = result.as_list();
        
        for (const auto& entry : fs::directory_iterator(path)) {
            files.push_back(Value::string(entry.path().filename().string()));
        }
        
        return result;
    } catch (const std::exception& e) {
        throw std::runtime_error("dir_list() failed: " + std::string(e.what()));
    }
}

Value dir_walk_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("dir_walk() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "dir_walk");
    
    try {
        if (!fs::exists(path)) {
            throw std::runtime_error("dir_walk() failed: directory not found: " + path);
        }
        
        if (!fs::is_directory(path)) {
            throw std::runtime_error("dir_walk() failed: not a directory: " + path);
        }
        
        Value result = Value::list();
        auto& files = result.as_list();
        
        for (const auto& entry : fs::recursive_directory_iterator(path)) {
            if (fs::is_regular_file(entry.path())) {
                files.push_back(Value::string(entry.path().string()));
            }
        }
        
        return result;
    } catch (const std::exception& e) {
        throw std::runtime_error("dir_walk() failed: " + std::string(e.what()));
    }
}

// ========================================
// 文件操作工具实现
// ========================================

Value file_copy_builtin(const std::vector<Value>& args) {
    if (args.size() != 2) {
        throw std::runtime_error("file_copy() requires exactly 2 arguments");
    }
    
    std::string src = validate_path_arg(args[0], "file_copy");
    std::string dst = validate_path_arg(args[1], "file_copy");
    
    try {
        if (!fs::exists(src)) {
            throw std::runtime_error("file_copy() failed: source file not found: " + src);
        }
        
        fs::copy_file(src, dst, fs::copy_options::overwrite_existing);
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("file_copy() failed: " + std::string(e.what()));
    }
}

Value file_move_builtin(const std::vector<Value>& args) {
    if (args.size() != 2) {
        throw std::runtime_error("file_move() requires exactly 2 arguments");
    }
    
    std::string src = validate_path_arg(args[0], "file_move");
    std::string dst = validate_path_arg(args[1], "file_move");
    
    try {
        if (!fs::exists(src)) {
            throw std::runtime_error("file_move() failed: source file not found: " + src);
        }
        
        fs::rename(src, dst);
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("file_move() failed: " + std::string(e.what()));
    }
}

Value file_delete_builtin(const std::vector<Value>& args) {
    if (args.size() != 1) {
        throw std::runtime_error("file_delete() requires exactly 1 argument");
    }
    
    std::string path = validate_path_arg(args[0], "file_delete");
    
    try {
        if (!fs::exists(path)) {
            throw std::runtime_error("file_delete() failed: file not found: " + path);
        }
        
        fs::remove(path);
        return Value::boolean(true);
    } catch (const std::exception& e) {
        throw std::runtime_error("file_delete() failed: " + std::string(e.what()));
    }
}

} // namespace viml
